﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;

namespace Borg
{
    public static class Runtime
    {
        internal static Type[] contractEventHandlerParamTypes =
            new[]
            {
                typeof (object),
                typeof (ContractEventArgs)
            };

        internal static Type[] pluginEventHandlerParamTypes =
            new[]
            {
                typeof (object),
                typeof (PluginEventArgs)
            };

        internal static Type[] extensionEventHandlerParamTypes =
            new[]
            {
                typeof (object),
                typeof (ExtensionEventArgs)
            };

        internal static Type[] cancelExtensionEventHandlerParamTypes =
            new[]
            {
                typeof (object),
                typeof (CancelExtensionEventArgs)
            };

        internal static Type[] registerEventHandlerParamTypes =
            new[]
            {
                typeof (object),
                typeof (RegisterEventArgs)
            };

        internal static Type[] cancelRegisterEventHandlerParamTypes =
            new[]
            {
                typeof (object),
                typeof (CancelRegisterEventArgs)
            };

        internal static Type[] plugEventHandlerParamTypes =
            new[]
            {
                typeof (object),
                typeof (PlugEventArgs)
            };

        internal static Type[] cancelPlugEventHandlerParamTypes =
            new[]
            {
                typeof (object),
                typeof (CancelPlugEventArgs)
            };

        internal static Type[] slotEventHandlerParamTypes =
            new[]
            {
                typeof (object),
                typeof (SlotEventArgs)
            };

        internal static Type[] cancelSlotEventHandlerParamTypes =
            new[]
            {
                typeof (object),
                typeof (CancelSlotEventArgs)
            };

        internal static Type[] taskEventHandlerParamTypes =
            new[]
            {
                typeof (object),
                typeof (TaskEventArgs)
            };

        internal static Type[] selectionChangedHandlerParamTypes =
            new[]
            {
                typeof (object),
                typeof (SelectionChangedEventArgs)
            };

        internal static RuntimeBase runtime;
        internal static Assembly assembly;

        public static RuntimeBase Instance
        {
            get { return runtime.Instance; }
        }

        public static bool IsRunning
        {
            get { return runtime != null; }
        }

        public static Assembly Assembly
        {
            get { return assembly; }
        }

        public static DirectoryInfo RootDirectory
        {
            get { return Instance.rootDirectory; }
        }

        public static Repository Repository
        {
            get { return Instance.Repository; }
        }

        public static TaskQueue TaskQueue
        {
            get { return Instance.TaskQueue; }
        }

        public static Composer Composer
        {
            get { return Instance.Composer; }
        }

        public static LogBuffer LogBuffer
        {
            get { return Instance.logBuffer; }
        }

        public static VerbosityLevel VerbosityLevel
        {
            get { return Instance.VerbosityLevel; }
            set { Instance.VerbosityLevel = value; }
        }

        public static TimeSpan UpTime
        {
            get { return Instance.UpTime; }
        }

        public static bool AutoOpen
        {
            get { return Instance.AutoOpen; }
            set { Instance.AutoOpen = value; }
        }

        public static bool AutoRegister
        {
            get { return Instance.AutoRegister; }
            set { Instance.AutoRegister = value; }
        }

        public static bool AutoPlug
        {
            get { return Instance.AutoPlug; }
            set { Instance.AutoPlug = value; }
        }

        public static bool AutoRelease
        {
            get { return Instance.AutoRelease; }
            set { Instance.AutoRelease = value; }
        }

        public static bool LazyLoad
        {
            get { return Instance.LazyLoad; }
            set { Instance.LazyLoad = value; }
        }

        public static bool IsCompositionAllowed
        {
            get { return Instance.IsCompositionAllowed; }
        }

        public static Dictionary<string, string> Arguments
        {
            get { return Instance.arguments; }
        }

        public static ICollection<string> BaseDirectories
        {
            get { return Instance.baseDirectories; }
        }

        public static bool ConsoleWindow
        {
            get { return Instance.ConsoleWindow; }
            set { Instance.ConsoleWindow = value; }
        }

        public static ExtensionInfo ExtensionInfo
        {
            get { return Instance.ExtensionInfo; }
        }

        internal static bool IsFinalizing
        {
            get { return Instance.isFinalizing; }
        }

        public static event LogEventHandler LogEvent
        {
            add { Instance.LogEvent += value; }
            remove { Instance.LogEvent -= value; }
        }

        public static event PropertyChangedEventHandler PropertyChanged
        {
            add { Instance.PropertyChanged += value; }
            remove { Instance.PropertyChanged -= value; }
        }

        public static event EventHandler StartupCompleted
        {
            add { Instance.StartupCompleted += value; }
            remove { Instance.StartupCompleted -= value; }
        }

        static Runtime()
        { }

        public static bool IsRuntimeThread()
        {
            return Instance.IsRuntimeThread();
        }

        public static object Invoke(Delegate method, params object[] args)
        {
            return Instance.Invoke(method, args);
        }

        public static Composer DisableComposition()
        {
            return Instance.DisableComposition();
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        public static void Shutdown()
        {
            if (Instance.IsRuntimeThread())
                InternalShutdown();
            else
                Instance.Invoke(new NoParameterHandler(InternalShutdown), new object[0]);
        }

        private static void InternalShutdown()
        {
            Instance.TaskQueue.Enqueue(
                TaskFactory.CreateShutdown(
                    TaskFactory.CreateTaskGroupId(),
                    Assembly.GetCallingAssembly()));
        }

        internal static void OnLogEvent(object sender, LogEventArgs args)
        {
            Instance.OnLogEvent(sender, args);
        }

        internal static void OnPropertyChanged(object sender, string propertyName)
        {
            Instance.OnPropertyChanged(sender, new PropertyChangedEventArgs(propertyName));
        }

        internal static void OnStartupCompleted(object sender, EventArgs args)
        {
            Instance.OnStartupCompleted(sender, args);
        }

        internal static void InvokeEventHandler(string methodDescription,
            MulticastDelegate multicastDelegate, object sender, object args)
        {
            Instance.InvokeEventHandler(methodDescription, multicastDelegate, sender, args);
        }

        internal static void InvokeCancelEventHandler(string methodDescription,
            MulticastDelegate mulitcastDelegate, object sender, ICancelEventArgs args)
        {
            Instance.InvokeCancelEventHandler(methodDescription, mulitcastDelegate, sender, args);
        }

        internal static void InternalShutdown(Assembly initiator)
        {
            Instance.Shutdown(initiator);
        }

        public static string VersionInfo()
        {
            var builder = new StringBuilder();
            var executingAssembly = Assembly.GetExecutingAssembly();

            builder.AppendFormat("{0} Version {1}{2}",
                GetAssemblyTitle(executingAssembly),
                GetAssemblyFileVersion(executingAssembly),
                Environment.NewLine);

            builder.AppendFormat("[{0} Version {1}]{2}",
                GetAssemblyProduct(executingAssembly),
                GetAssemblyInformationalVersion(executingAssembly),
                Environment.NewLine);

            builder.AppendLine(GetAssemblyCopyright(executingAssembly));

            return builder.ToString();
        }

        public static ExtensionInfo GetExtensionInfo(object o)
        {
            return Instance.Repository.objects.ContainsKey(o)
                ? Instance.Repository.objects[o]
                : null;
        }

        public static SlotInfo GetSlotInfo(object o, string name)
        {
            return Instance.Repository.objects.ContainsKey(o)
                ? Instance.Repository.objects[o].slotInfos[name]
                : null;
        }

        public static SlotInfo GetSlotInfoByDerivedFrom(object o, string derivedFrom)
        {
            return !Instance.Repository.objects.ContainsKey(o)
                       ? null
                       : (from slotInfo in Instance.Repository.objects[o].slotInfos
                          let derivedFrm = slotInfo.SlotTypeInfo.DerivedFrom
                          where derivedFrm != null && derivedFrm.Equals(derivedFrom)
                          select slotInfo).FirstOrDefault();
        }

        public static SlotInfo GetSlotInfo(object o, int index)
        {
            return Instance.Repository.objects.ContainsKey(o)
                ? Instance.Repository.objects[o].slotInfos[index]
                : null;
        }

        public static PlugInfo GetPlugInfo(object o, string name)
        {
            return Instance.Repository.objects.ContainsKey(o)
                ? Instance.Repository.objects[o].plugInfos[name]
                : null;
        }

        public static PlugInfo GetPlugInfo(object o, int index)
        {
            return Instance.Repository.objects.ContainsKey(o)
                ? Instance.Repository.objects[o].plugInfos[index]
                : null;
        }

        public static PlugInfo GetPlugInfo(object o, Type t)
        {
            return !Instance.Repository.objects.ContainsKey(o)
                ? null
                : Instance.Repository.objects[o].plugInfos.
                    FirstOrDefault(plugInfo => plugInfo.SlotDefinition.Type == t);
        }

        public static SlotTypeInfo GetSlotTypeInfo(object o, string name)
        {
            return Instance.Repository.objects.ContainsKey(o)
                ? Instance.Repository.objects[o].slotInfos[name].slotTypeInfo
                : null;
        }

        public static SlotTypeInfo GetSlotTypeInfo(object o, int index)
        {
            return Instance.Repository.objects.ContainsKey(o)
                ? Instance.Repository.objects[o].slotInfos[index].slotTypeInfo
                : null;
        }

        public static PlugTypeInfo GetPlugTypeInfo(object o, string name)
        {
            return Instance.Repository.objects.ContainsKey(o)
                ? Instance.Repository.objects[o].plugInfos[name].plugTypeInfo
                : null;
        }

        public static PlugTypeInfo GetPlugTypeInfo(object o, Type t)
        {
            return !Instance.Repository.objects.ContainsKey(o)
                       ? null
                       : (from plugInfo in Instance.Repository.objects[o].plugInfos
                          where plugInfo.PlugTypeInfo.SlotDefinition.Type.FullName.Equals(t.FullName)
                          select plugInfo.PlugTypeInfo).FirstOrDefault();
        }

        public static PlugTypeInfo GetPlugTypeInfo(object o, int index)
        {
            return Instance.Repository.objects.ContainsKey(o)
                ? Instance.Repository.objects[o].plugInfos[index].plugTypeInfo
                : null;
        }

        public static ExtensionTypeInfo GetExtensionTypeInfo(object o)
        {
            return Instance.Repository.objects.ContainsKey(o)
                ? Instance.Repository.objects[o].extensionTypeInfo
                : null;
        }

        public static PluginInfo GetPluginInfo(object o)
        {
            return Instance.Repository.objects.ContainsKey(o)
                ? Instance.Repository.objects[o].extensionTypeInfo.pluginInfo
                : null;
        }

        public static PluginInfo[] GetPluginInfos(string path)
        {
            var uri = new Uri(path);

            return (from repoId in Instance.Repository.plugins.Keys
                    where repoId.Uri.AbsoluteUri.Equals(uri.AbsoluteUri)
                    select Instance.Repository.plugins[repoId]).ToArray();
        }

        public static ContractInfo[] GetContractInfos(string path)
        {
            var uri = new Uri(path);

            return (from repoId in Instance.Repository.contracts.Keys
                    where repoId.Uri.AbsoluteUri.Equals(uri.AbsoluteUri)
                    select Instance.Repository.contracts[repoId]).ToArray();
        }

        private static string GetAssemblyTitle(Assembly a)
        {
            return ((AssemblyTitleAttribute)a.
                GetCustomAttributes(typeof(AssemblyTitleAttribute), false)[0]).Title;
        }

        private static string GetAssemblyProduct(Assembly a)
        {
            return ((AssemblyProductAttribute)a.
                GetCustomAttributes(typeof(AssemblyProductAttribute), false)[0]).Product;
        }

        private static string GetAssemblyInformationalVersion(Assembly a)
        {
            return ((AssemblyInformationalVersionAttribute)a.
                GetCustomAttributes(typeof(AssemblyInformationalVersionAttribute), false)[0]).InformationalVersion;
        }

        private static string GetAssemblyFileVersion(Assembly a)
        {
            return ((AssemblyFileVersionAttribute)a.
                GetCustomAttributes(typeof(AssemblyFileVersionAttribute), false)[0]).Version;
        }

        private static string GetAssemblyCopyright(Assembly a)
        {
            return ((AssemblyCopyrightAttribute)a.
                GetCustomAttributes(typeof(AssemblyCopyrightAttribute), false)[0]).Copyright;
        }
    }
}

